/*
 * Mesh.cpp
 *
 *  Created on: 29 mars 2011
 *      Author: seeme
 */

#include "Mesh.h"
#include "../../../Tools/Geometry/Matrix4.h"
#include "../../../Tools/Misc/Singleton.h"
#include "../../ResCache.h"
#include "../TextBased/Shaders/Shader.h"
#include <iostream>


GLfloat* Mesh::getCoordTab(){
	//Revoie un tableau de float pour passer à openGL.
	if(NULL == m_vertice){

		m_vertice = new float[m_points.size() * 3];
		if(m_vertice){
			for(unsigned int i = 0; i < m_points.size(); ++i){
				m_vertice[3*i] = m_points[i].x;
				m_vertice[3*i+1] = m_points[i].y;
				m_vertice[3*i+2] = m_points[i].z;
			}
		}else{
			Log::e("Mesh") << "Allocation error";
			//Never reached...
			return NULL;
		}
	}
	return m_vertice;
}

GLuint Mesh::getProgramId(std::string name){

	std::map<std::string, GLuint>::iterator found;
	found = m_programs.find(name);
	if(found != m_programs.end()){
		return found->second;
	}
	Log::i("Mesh") << "Program " << name << " not found";
	return -1;
}

GLfloat* Mesh::getUvTab(){
	//Revoie un tableau de float pour passer à openGL.
	if(NULL == m_uv){

		m_uv = new float[m_points.size() * 2];
		if(m_uv){
			for(unsigned int i = 0; i < m_points.size(); ++i){
				m_uv[2*i] = m_points[i].u;
				m_uv[2*i+1] = m_points[i].v;
			}
		}else{
			Log::e("Mesh") << "Allocation error";
			//Never reached...
			return NULL;
		}
	}
	return m_uv;
}


GLuint Mesh::getUvTabSize(){
	return m_points.size()*2;
}

GLuint Mesh::getCoordTabSize(){
	return m_points.size()*3;
}

Material* Mesh::getMaterial(){
	return m_material;
}

void Mesh::sendMaterial(GLuint program){
	GLuint color = glGetUniformLocation(program, "diffColor");
	GLuint reflexion = glGetUniformLocation(program, "reflexion");
	GLuint shin = glGetUniformLocation(program, "shininess");
	GLuint refract = glGetUniformLocation(program, "refraction");

	glUniform4f(color, m_material->getColor().x,
									   m_material->getColor().y,
									   m_material->getColor().z,
									   m_material->getColor().w);

	glUniform3f(reflexion, m_material->getReflexion().x,
									   	   m_material->getReflexion().y,
									   	   m_material->getReflexion().z);

	glUniform1f(shin, m_material->getShininess());
	glUniform1f(refract, m_material->getRefraction());

}

void Mesh::destroy(){
	m_points.clear();
	m_triangles.clear();

	m_size = 0;
	if(m_material)
		delete m_material;
}

void Mesh::sendTexture(GLuint program){
	//Log::i("sfdg") << m_name;

	if(m_texture){
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, m_texture->getId());
		//Log::i("sfdg") << m_name << ", " << m_texture->getId();
		glUniform1i(glGetUniformLocation(program, "mainTexture"), GL_TEXTURE0);
	}
}

GLuint* Mesh::getIndexTab(){
	//Revoie un tableau de float pour passer à openGL.
	if(NULL == m_index){
		m_index = new unsigned int[m_triangles.size() * 3];
		if(m_index){
			for(unsigned int i = 0; i < m_triangles.size(); ++i){
				m_index[3*i] = m_triangles[i].m_p1;
				m_index[3*i+1] = m_triangles[i].m_p2;
				m_index[3*i+2] = m_triangles[i].m_p3;
			}
		}else{
			Log::e("Mesh") << "Allocation error";
			//Never reached...
			return NULL;
		}
	}
	return m_index;
}

GLuint Mesh::getIndexTabSize(){
	return m_triangles.size()*3;
}

GLfloat* Mesh::getNormTab(){
	//Revoie un tableau de float pour passer à openGL.
	if(NULL == m_normals){

		m_normals = new float[m_points.size() * 3];
		if(m_normals){
			for(unsigned int i = 0; i < m_points.size(); ++i){
				m_normals[3*i] = m_points[i].nx;
				m_normals[3*i+1] = m_points[i].ny;
				m_normals[3*i+2] = m_points[i].nz;
			}
		}else{
			Log::e("Mesh") << "Allocation error";
			//Never reached...
			return NULL;
		}
	}
	return m_normals;
}

GLuint Mesh::getNormTabSize(){
	return m_points.size()*3;
}


void Mesh::loadFromFile(){
	if(!m_filePath.empty()){
		//On ouvre le fichier:
		openFile();
		TiXmlHandle hdl(&m_data);
		TiXmlElement *object = hdl.FirstChildElement().Element();
		TiXmlElement *next, *col, *reflex;

		Vect4<float> normal;

		Matrix4<float> mat;
		std::string name, sName;

		float shininess, refraction;
		Vect4<float> color, reflexion;

		std::vector<Point> points, coordTex;
		Point p;


		std::vector<Triangle<unsigned int> > tris;
		Triangle<unsigned int> t;

		std::string vShader, fShader;

		ResCache * s = Singleton<ResCache>::get();



		if(strcmp(object->Attribute("type"),"Mesh") == 0){
			points.clear();
			tris.clear();
			name = object->Attribute("name");

			//On lit les shaders
			next = object->FirstChildElement("shader");
			while(next){
				sName = next->Attribute("name");
				vShader = next->Attribute("v");
				fShader = next->Attribute("f");

				Shader* vs = (Shader*)s->getResource(vShader);
				Shader* fs = (Shader*)s->getResource(fShader);

				Program* program = new Program(vs->getShaderId(), fs->getShaderId());
				m_programs.insert(std::pair<std::string, GLuint>(sName, program->getId()));

				vs->downRefCount();
				fs->downRefCount();
				delete program;

				next = next->NextSiblingElement("shader");
			}

			//On lit la texture
			next = object->FirstChildElement("texture");
			if(next){
				m_texture = (Texture*)s->getResource(next->Attribute("id"));
			}

			//On lis les vertex
			next = object->FirstChildElement("vertex");
			while(next){
				p.x = atof(next->Attribute("x"));
				p.y = atof(next->Attribute("y"));
				p.z = atof(next->Attribute("z"));
				p.u = atof(next->Attribute("u"));
				p.v = atof(next->Attribute("v"));
				p.nx = atof(next->Attribute("nox"));
				p.ny = atof(next->Attribute("noy"));
				p.nz = atof(next->Attribute("noz"));

				//Log::i("Mesh") << p.nx << ", " << p.ny << ", " << p.nz;

				//On assume que les points sont dans l'ordre dans le fichier
				points.push_back(p);

				next = next->NextSiblingElement("vertex");
			}

			//On lit les faces et les normales...
			next = object->FirstChildElement("face");
			while(next){
				t.m_p1 = atoi(next->Attribute("id1"));
				t.m_p2 = atoi(next->Attribute("id2"));
				t.m_p3 = atoi(next->Attribute("id3"));

				tris.push_back(t);

				next = next->NextSiblingElement("face");
			}

			//On lit les propriétés du matériau
			next = object->FirstChildElement("material");
			shininess = atof(next->Attribute("shininess"));
			refraction = atof(next->Attribute("refraction"));


			col = next->FirstChildElement("color");
			color.x = atof(col->Attribute("r"));
			color.y = atof(col->Attribute("g"));
			color.z = atof(col->Attribute("b"));
			color.w = atof(col->Attribute("a"));

			reflex = next->FirstChildElement("reflexion");
			reflexion.x = atof(reflex->Attribute("r"));
			reflexion.y = atof(reflex->Attribute("g"));
			reflexion.z = atof(reflex->Attribute("b"));

			m_material = new Material(shininess, refraction, reflexion, color);
		}
		//On calcul les normales
		m_triangles = tris;
		m_points = points;

		m_size = m_triangles.size() * 3 * sizeof(Point);
	}
}

void Mesh::addProgram(std::string name, Program* prog){
	if(prog){
		m_programs.insert(std::pair<std::string, GLuint>(name, prog->getId()));
	}
}
